package com.zmj.sy.mom.srv.aps.service.scheduler.resource.shift;

import lombok.Getter;
import lombok.Setter;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/**
 * 时段，包括起止时间点，起止时间相同时，表示时间点
 */
@Getter
@Setter
public class TimeRange {

    private static final TimeRange EMPTY = of();

    private LocalDateTime startTime;

    private LocalDateTime endTime;

    private static TimeRange of(){
        TimeRange timeRange = new TimeRange();
        timeRange.setStartTime(LocalDateTime.MAX);
        timeRange.setEndTime(LocalDateTime.MIN);
        return timeRange;
    }

    public static TimeRange of(LocalDateTime startTime, LocalDateTime endTime) {
        if (startTime.isAfter(endTime)) {
            return EMPTY;
        }
        TimeRange timeRange = new TimeRange();
        timeRange.setStartTime(startTime);
        timeRange.setEndTime(endTime);
        return timeRange;
    }

    public static LocalDateTime max(LocalDateTime... times) {
        return Stream.of(times)
                .max(LocalDateTime::compareTo)
                .orElseThrow(() -> new RuntimeException("没有时间"));
    }

    public static LocalDateTime min(LocalDateTime... times) {
        return Stream.of(times)
                .min(LocalDateTime::compareTo)
                .orElseThrow(() -> new RuntimeException("没有时间"));
    }

    public static TimeRange empty() {
        return EMPTY;
    }

    public boolean isEmpty() {
        return startTime.isAfter(endTime);
    }

    public Duration length() {
        if (this.isEmpty()) {
            return Duration.ZERO;
        }
        return Duration.of(ChronoUnit.SECONDS.between(startTime, endTime), ChronoUnit.SECONDS);
    }

    public boolean contains(LocalDateTime time) {
        return !time.isBefore(startTime) && !time.isAfter(endTime);
    }

    /**
     * 交集
     */
    public TimeRange intersect(TimeRange other) {
        if (this.isEmpty() || other == null || other.isEmpty()) {
            return empty();
        }
        return of(
                max(this.getStartTime(), other.getStartTime()),
                min(this.getEndTime(), other.getEndTime())
        );
    }

    /**
     * 并集
     */
    public MultiTimeRange union(TimeRange other) {
        if (this.isEmpty()) {
            if (other == null) {
                return MultiTimeRange.empty();
            }
            return MultiTimeRange.of(Collections.singletonList(other), true, true);
        }
        if (other == null || other.isEmpty()) {
            return MultiTimeRange.of(Collections.singletonList(this), true, true);
        }
        if (this.startTime.isAfter(other.endTime)) {
            return MultiTimeRange.of(Arrays.asList(other, this), true, true);
        }
        if (this.endTime.isBefore(other.startTime)) {
            return MultiTimeRange.of(Arrays.asList(this, other), true, true);
        }
        LocalDateTime startTime = min(this.getStartTime(), other.getStartTime());
        LocalDateTime endTime = max(this.getEndTime(), other.getEndTime());
        return MultiTimeRange.of(Collections.singletonList(of(startTime, endTime)), true, true);
    }

    /**
     * 差集
     */
    public MultiTimeRange subtract(TimeRange other) {
        if (this.isEmpty()) {
            return MultiTimeRange.empty();
        }
        if (other.isEmpty()) {
            return MultiTimeRange.of(Collections.singletonList(this), true, true);
        }

        if (this.startTime.isAfter(other.endTime) || this.endTime.isBefore(other.startTime)) {
            return MultiTimeRange.of(Collections.singletonList(this), true, true);
        }

        List<TimeRange> result = new ArrayList<>();
        // A在B之前的部分
        if (this.startTime.isBefore(other.startTime)) {
            TimeRange timeRange = TimeRange.of(this.startTime, other.startTime.minusSeconds(1));
            if (!timeRange.isEmpty()) {
                result.add(timeRange);
            }
        }

        // A在B之后的部分
        if (this.endTime.isAfter(other.endTime)) {
            TimeRange timeRange = TimeRange.of(other.endTime.plusSeconds(1), this.endTime);
            if (!timeRange.isEmpty()) {
                result.add(timeRange);
            }
        }

        return MultiTimeRange.of(result, true, true);
    }

    public TimeRange lengthBefore(Duration length, LocalDateTime endTime) {
        if (!endTime.isAfter(this.startTime)) {
            return empty();
        }
        LocalDateTime rangeEndTime = min(this.endTime, endTime);
        LocalDateTime rangeStartTime = max(this.startTime, rangeEndTime.minus(length));
        return TimeRange.of(rangeStartTime, rangeEndTime);
    }

}
